class DepartmentAbbreviation < ActiveRecord::Base
  validates_presence_of :department
  validates_presence_of :abbreviation
  
  def DepartmentAbbreviation.add_unless_dup(department, abbreviation)
    return if DepartmentAbbreviation.count(:all, :conditions => {:department => department.upcase, :abbreviation => abbreviation}) > 0
    DepartmentAbbreviation.create!(:department => department.upcase, :abbreviation => abbreviation)
  end
  
  def DepartmentAbbreviation.list_of_departments
    list = DepartmentAbbreviation.find(:all)
    return (list.collect { |item| item.department}).uniq
  end

  def DepartmentAbbreviation.find_department(name)
    name = name.upcase
    return name if Course.count(:all, :conditions => {:department => name}) > 0

    department = DepartmentAbbreviation.find(:first, :conditions => {:abbreviation => name})
    return department.department if department

    return nil
  end

  def DepartmentAbbreviation.find_abbreviations(name)
    abbreviations = DepartmentAbbreviation.find(:all, :conditions => {:department => name.upcase})
    return abbreviations.collect {|item| item.abbreviation}
  end

  def DepartmentAbbreviation.fetch_and_assign_abbreviations
    departments = DepartmentAbbreviation.fetch_departments
    abbreviations = DepartmentAbbreviation.fetch_abbreviation_pairs
    dept_without_abbr = []

    # If department names match, immediately create abbreviation
    departments.each do |dept|
      if abbreviations[dept]
        add_unless_dup(dept, abbreviations[dept])
        abbreviations.delete(dept)
      else
        dept_without_abbr.push(dept)
      end
    end

    # If department names didn't match, use some heuristics to determine which department names are the closest.
    dept_without_abbr.each do |dept|
      s_dept = Set.new(dept.upcase.split(/\s+/))
      dept_intersection = []
      abbreviations.each_pair do |abbr_dept,abbr|
        s_abbr = Set.new(abbr_dept.upcase.split(/\s+/))
        size_of_intersection = (s_dept & s_abbr).size
        # Compute the number of words identical in departments and abbreviations
        # While the size of intersection gives a good heuristics, this is not adquate.
        # Consider the case where we have dept="Business Administration Evening/Weekend MBA Program"
        # we have two options: "Evening/Weekend MBA Program" and
        # "Undergraduate Program leading to the Bachelor of Science in Business Administration"
        # If we only used the size of intersection, both of them are equally likely since both shares 3 words.
        # But the first name clearly matches better with the given department name.
        # To resolve this problem, add a small bias towards the name with a higher intersection / length ratio.
        dept_intersection.push([size_of_intersection + size_of_intersection*3/s_abbr.size, abbr, abbr_dept])
      end
      add_unless_dup(dept, dept_intersection.max[1])
    end
    # We avoid using this heuristics approach for all departments and abbreviation pairs because
    # Computation time for the size of intersection increases quadratic to the size of two lists.

    return departments
  end

  private

  def DepartmentAbbreviation.fetch_departments
    begin
      fh = open('http://schedule.berkeley.edu/srchfall.html')
    rescue
      return nil
    end
    doc = Hpricot(fh)
    list = (doc/"select[@name='p_deptname']/option").collect { |opt| opt.innerText.strip }
    return list.select {|name| not name.match(/Choose.+Department/i)}
  end

  def DepartmentAbbreviation.fetch_abbreviation_pairs
    begin
      fh = open('http://registrar.berkeley.edu/Default.aspx?PageID=deptabb.html')
    rescue
      return nil
    end

    result = {}

    doc = Hpricot(fh)
    (doc/"#deptAbb/tbody/tr").each do |tr|
      tds = (tr/'td')
      next if tds.size != 2
      departments = tds[0].innerText.split(/\s*\n\s*/)
      abbreviations = tds[1].innerText.split(/\s*\n\s*/)
      next if departments.size != abbreviations.size
      departments.each_with_index do |dept, i|
        next if dept.length <= 0
        result[dept] = abbreviations[i].gsub(/\s|[^x0-x7f]/,'')
      end
    end

    return result
  end

end
